import time

from gluon.cache import CacheAbstract
from gluon.contrib.memcache.memcache import Client

"""
examle of usage:

cache.memcache = MemcacheClient(request,[127.0.0.1:11211],debug=true)
"""

import cPickle as pickle
import thread

from gluon import current

DEFAULT_TIME_EXPIRE = 300  # seconds (must be the same as cache.ram)


def MemcacheClient(*a, **b):
    if not hasattr(current, "__memcache_client"):
        current.__memcache_client = MemcacheClientObj(*a, **b)
    return current.__memcache_client


class MemcacheClientObj(Client):
    def initialize(self):
        pass

    meta_storage = {}
    max_time_expire = 24 * 3600

    def __init__(
        self,
        request,
        servers,
        debug=0,
        pickleProtocol=0,
        pickler=pickle.Pickler,
        unpickler=pickle.Unpickler,
        pload=None,
        pid=None,
        default_time_expire=DEFAULT_TIME_EXPIRE,
    ):
        self.request = request
        self.default_time_expire = default_time_expire
        if request:
            app = request.application
        else:
            app = ""
        Client.__init__(
            self, servers, debug, pickleProtocol, pickler, unpickler, pload, pid
        )
        if not app in self.meta_storage:
            self.storage = self.meta_storage[app] = {
                CacheAbstract.cache_stats_name: {
                    "hit_total": 0,
                    "misses": 0,
                }
            }
        else:
            self.storage = self.meta_storage[app]

    def __call__(self, key, f, time_expire="default"):
        if time_expire == "default":
            time_expire = self.default_time_expire
        if time_expire == None:
            time_expire = self.max_time_expire
        # this must be commented because get and set are redefined
        # key = self.__keyFormat__(key)
        now = time.time()
        value = None
        if f is None:  # force deletion of value
            self.delete(key)
            return None
        elif time_expire == 0:  # value forced expired
            item = None  # value to be computed
        else:
            item = self.get(key)
            if item:
                if not isinstance(item, (list, tuple)):
                    value = item
                elif item[0] < now - time_expire:  # value expired
                    item = None  # value to be computed
                else:
                    value = item[1]
        if not item:
            value = f()
            self.set(key, (now, value), self.max_time_expire)
        return value

    def increment(self, key, value=1, time_expire="default"):
        """time_expire is ignored"""
        if time_expire == "default":
            time_expire = self.default_time_expire
        newKey = self.__keyFormat__(key)
        obj = Client.get(self, newKey)
        if obj:
            if isinstance(obj, (int, float, long)):
                return Client.incr(self, newKey, value)
            else:
                value += obj[1]
                Client.set(self, newKey, (time.time(), value), self.max_time_expire)
                return value
        else:
            Client.set(self, newKey, value, self.max_time_expire)
            return value

    def set(self, key, value, time_expire="default"):
        if time_expire == "default":
            time_expire = self.default_time_expire
        newKey = self.__keyFormat__(key)
        return Client.set(self, newKey, value, time_expire)

    def get(self, key):
        newKey = self.__keyFormat__(key)
        return Client.get(self, newKey)

    def delete(self, key):
        newKey = self.__keyFormat__(key)
        return Client.delete(self, newKey)

    def __keyFormat__(self, key):
        return "%s/%s" % (self.request.application, key.replace(" ", "_"))
